package cifa;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;


/**
 * @创建人 MBY381
 * @创建时间 2022/6/25 14:18
 * @描述 暂无
 */
public class CIFFAAnalysis {


    private BufferedReader reader;
    private char[] scanBuffer;
    private char[] backBuffer;
    private int position = 0;
    private int lineNum = 1;
    private int colNum = 1;
    private StringBuffer strToken;
    private List<CharMark> markTable;
    private List<CharMark> constTable;
    private Properties keyWord;
    private boolean fileFlag;

    private final static int BUFFER_SIZE = 5;

    public List<CharMark> getMarkTable() {
        return markTable;
    }

    public void setMarkTable(List<CharMark> markTable) {
        this.markTable = markTable;
    }

    public List<CharMark> getConstTable() {
        return constTable;
    }

    public void setConstTable(List<CharMark> constTable) {
        this.constTable = constTable;
    }

    public int getLineNum() {
        return lineNum;
    }

    public int getColNum() {
        return colNum;
    }


    public CIFFAAnalysis() {
        super();
    }

    public CIFFAAnalysis(BufferedReader reader) {
        super();
        this.scanBuffer = new char[BUFFER_SIZE];
        this.backBuffer = new char[BUFFER_SIZE];
        this.strToken = new StringBuffer();
        this.keyWord = new Properties();
        this.markTable = new ArrayList<CharMark>();
        this.constTable = new ArrayList<CharMark>();
        this.reader = reader;
        this.fileFlag = true;
        analysisProcess();

        try {
            CIFFAAnalysis.class.getClassLoader();
            keyWord.load(ClassLoader.getSystemResourceAsStream("keyword.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //查找保留字表
    private int reserveTableCheck(String value) {
        int result = 0;
        for (int i = 1; i <= keyWord.size(); i++) {
            String temp = keyWord.getProperty(Integer.toString(i));
            if (value.equalsIgnoreCase(temp))
                result = i;
        }
        return result;
    }

    //插入标识符表
    private int markTableCheck(String value) {
        return tiqu(value, markTable);
    }

    private int tiqu(String value, List<CharMark> identifierTable) {
        int result = 0;
        boolean flag = true;
        for (int i = 0; i < identifierTable.size(); i++) {
            CharMark temp = identifierTable.get(i);
            if (temp.getValue().equalsIgnoreCase(value)) {
                flag = false;
                result = temp.getKey();
            }
        }
        if (flag) {
            result = identifierTable.size() + 1;
            identifierTable.add(new CharMark(identifierTable.size() + 1, value));
        }
        return result;
    }

    //插入标识符表
    private int insertConst(String value) {
        return tiqu(value, constTable);
    }

    //预处理过程，将数据读入缓冲区
    private void analysisProcess() {
        int result = 0;
        try {
            //清空scanBuffer
            Arrays.fill(scanBuffer, ' ');
            //标记流 便于回退
            reader.mark(scanBuffer.length);
            result = reader.read(scanBuffer, 0, scanBuffer.length);
            //文件若已读完
            if (result == -1) {
                fileFlag = false;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //将指示器前移一位
    private void next() {
        position++;
        colNum++;
        if (position == scanBuffer.length) {
            backBuffer = scanBuffer.clone();
            analysisProcess();
            position = 0;
        }
    }

    //将指示器后退一位
    private void pre() {
        position--;
        if (position == -1) {
            scanBuffer = backBuffer.clone();
            position = scanBuffer.length - 1;
            try {
                //将流也回退
                reader.reset();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public Pair analysis() throws NoteException, DigitException, WordException {
        Pair tuple = null;
        //清空strToken
        strToken.delete(0, strToken.length());

        outer:
        while (fileFlag) {
            //跳过空格符,跳格符和换行符
            if (scanBuffer[position] == ' ' || scanBuffer[position] == '\t' || scanBuffer[position] == '\r' || scanBuffer[position] == '\n') {
                if (scanBuffer[position] == '\r') {
                    lineNum++;
                    colNum = -1;
                }
                next();
            }

            //处理注释，除号
            else if (scanBuffer[position] == '/') {
                next();

                //处理行注释
                if (scanBuffer[position] == '/') {
                    while (scanBuffer[position] != '\r') {
                        next();
                    }
                }

                //处理段注释
                else if (scanBuffer[position] == '*') {
                    int line = lineNum;
                    int col = colNum;
                    boolean noteFlag = true;
                    check:
                    while (fileFlag) {
                        next();
                        //计算当前行数
                        if (scanBuffer[position] == '\r') {
                            lineNum++;
                            colNum = -1;
                            next();
                        }
                        //判断注释结束符
                        if (scanBuffer[position] == '*') {
                            next();
                            if (scanBuffer[position] == '/') {
                                next();
                                noteFlag = false;
                                break check;
                            } else pre();
                        }
                    }
                    if (noteFlag)
                        throw new NoteException(line, col);

                }

                //当做除号处理
                else {
                    //返回二元组
                    pre();
                    strToken.append(scanBuffer[position]);
                    tuple = new Pair(reserveTableCheck(strToken.toString()), 0);
                    break outer;
                }
            }


            //处理标识符和保留字
            else if (Character.isLetter(scanBuffer[position])) {
                while (Character.isLetter(scanBuffer[position]) || Character.isDigit(scanBuffer[position])) {
                    strToken.append(scanBuffer[position]);
                    next();
                }
                int result = reserveTableCheck(strToken.toString());
                if (result != 0) {
                    //处理保留字
                    tuple = new Pair(result, 0);
                    break outer;
                } else {
                    //处理标识符
                    tuple = new Pair(reserveTableCheck("标识符"), markTableCheck(strToken.toString()));
                    break outer;
                }
            }

            //处理数字
            else if (Character.isDigit(scanBuffer[position])) {
                while (Character.isDigit(scanBuffer[position])) {
                    strToken.append(scanBuffer[position]);
                    next();
                }
                if (Character.isLetter(scanBuffer[position]))
                    throw new DigitException(lineNum, colNum);
                else {
                    tuple = new Pair(reserveTableCheck("常数"), insertConst(strToken.toString()));
                    break outer;
                }
            }
            //处理其他
            else if (scanBuffer[position] == '+' || scanBuffer[position] == '-' || scanBuffer[position] == '*' ||
                    scanBuffer[position] == '(' || scanBuffer[position] == ')' || scanBuffer[position] == ',' ||
                    scanBuffer[position] == '.' || scanBuffer[position] == ';' || scanBuffer[position] == '=') {

                strToken.append(scanBuffer[position]);
                next();
                tuple = new Pair(reserveTableCheck(strToken.toString()), 0);
                break outer;
            } else if (scanBuffer[position] == '<') {
                strToken.append(scanBuffer[position]);
                next();
                if (scanBuffer[position] == '=' || scanBuffer[position] == '>') {
                    strToken.append(scanBuffer[position]);
                    next();
                    tuple = new Pair(reserveTableCheck(strToken.toString()), 0);
                } else {
                    tuple = new Pair(reserveTableCheck(strToken.toString()), 0);
                }
                break outer;
            } else if (scanBuffer[position] == '>') {
                strToken.append(scanBuffer[position]);
                next();
                if (scanBuffer[position] == '=') {
                    strToken.append(scanBuffer[position]);
                    next();
                    tuple = new Pair(reserveTableCheck(strToken.toString()), 0);

                } else {
                    tuple = new Pair(reserveTableCheck(strToken.toString()), 0);
                }
                break outer;
            } else if (scanBuffer[position] == ':') {
                strToken.append(scanBuffer[position]);
                next();
                if (scanBuffer[position] == '=') {
                    strToken.append(scanBuffer[position]);
                    next();
                    tuple = new Pair(reserveTableCheck(strToken.toString()), 0);
                } else {
                    throw new WordException(lineNum, colNum - 1);
                }
                break outer;
            } else {
                next();
                throw new WordException(lineNum, colNum - 1);

            }

        }

        return tuple;

    }


}